/** ver 0.1 - date 04-01-2008 - by Jeroen S
 * First implementation version
 */

package mazeAssignment;

class IntermediaryStep {

	private Maze maze;
	
	// Maximum number of times we try to knock down
	static final int MAX_TRY_KNOCKDOWN = 20;
	
	public IntermediaryStep(Maze maze)
	{
		this.maze = maze;
	}
	
	public boolean updateMaze(boolean doRandomExit, int wallsToKnock)
	{
		int xsize = maze.getSizeX();
		int ysize = maze.getSizeY();
		
		resetSolvestate(Maze.SOLVE_START);
		resetSolvestate(Maze.SOLVE_END);
		
		if(doRandomExit)
		{
			//sets start and end randomly
			maze.setSolveState(getRandNum(xsize), getRandNum(ysize), Maze.SOLVE_START);
			maze.setSolveState(getRandNum(xsize), getRandNum(ysize), Maze.SOLVE_END);
		} else {
			//sets start and end
			maze.setSolveState(0, 0, Maze.SOLVE_START);
			maze.setSolveState(xsize-1, ysize-1, Maze.SOLVE_END);
		}
		
		// Knock down a number of walls
		for(int i = 0; i < wallsToKnock; i++)
		{
			// Skip over elements without walls
			boolean foundElement = false;
			// Place a limit on the times we check.
			int numtries = 0;
			while(!foundElement && numtries < MAX_TRY_KNOCKDOWN)
			{
				numtries++;
				
				int xpos = getRandNum(xsize);
				int ypos = getRandNum(ysize);
				
				//prepare other variables for the search of the partner (start somewhere randomly
				boolean foundPartner= false;
				boolean doneNorth = false;
				boolean doneEast = false;
				boolean doneSouth = false;
				boolean doneWest = false;
				int partner = selectPartner(doneNorth, doneEast, doneSouth, doneWest);
				int numpartners = 0;
				
				//search for a location that is aside the ourlocation, but that has not been added to the group
				//(list) where "ourlocation" belongs to. if all 4 walls have been searched for and none prevail,
				//then it is time to try to find another one.
				//there are tests for (in order:) out of bounds, same group)
				while (!foundPartner && numpartners < 4)
				{
					
					switch (partner)
					{
					//0 = north
					case 0: 
						doneNorth = true;
						if(ypos==0)
						{
							partner = selectPartner(doneNorth, doneEast, doneSouth, doneWest);
							numpartners++;
							break;
						}
						if(maze.getNorth(xpos, ypos))
						{
							foundPartner = true;
							foundElement = true;
							maze.setNorth(xpos, ypos, false);
							maze.setSouth(xpos, ypos-1, false);
						} else {
							partner = selectPartner(doneNorth, doneEast, doneSouth, doneWest);
							numpartners++;
						}
						break;
					//1= east
					case 1:	
						doneEast = true;
						if(xpos==maze.getSizeX()-1)
						{
							partner = selectPartner(doneNorth, doneEast, doneSouth, doneWest);
							numpartners++;
							break;
						}
						if(maze.getEast(xpos, ypos))
						{
							foundPartner = true;
							foundElement = true;
							maze.setWest(xpos+1, ypos, false);
							maze.setEast(xpos, ypos, false);
						} else {
							partner = selectPartner(doneNorth, doneEast, doneSouth, doneWest);
							numpartners++;
						}
						break;
					//2= south
					case 2:	
						doneSouth = true;
						if(ypos==maze.getSizeY()-1)
						{
							partner = selectPartner(doneNorth, doneEast, doneSouth, doneWest);
							numpartners++;
							break;
						}
						if (maze.getSouth(xpos, ypos))
						{
							maze.setNorth(xpos, ypos+1, false);
							maze.setSouth(xpos, ypos, false);
							foundPartner = true;
						} else {
							partner = selectPartner(doneNorth, doneEast, doneSouth, doneWest);
							numpartners++;
						}
						break;
					//3= west
					case 3:	
						doneWest = true;
						if(xpos==0)
						{
							partner = selectPartner(doneNorth, doneEast, doneSouth, doneWest);
							numpartners++;
							break;
						}
						if(maze.getWest(xpos, ypos))
						{
							foundPartner = true;
							maze.setEast(xpos-1, ypos, false);
							maze.setWest(xpos, ypos, false);
						} else {
							partner = selectPartner(doneNorth, doneEast, doneSouth, doneWest);
							numpartners++;
						}
						break;
					case -1:
						numpartners = 4;
						break;
					}
					
				}
				//if all walls have been searched, then it is time for a new element to checkup!
				if(numpartners > 3)
				{
					continue;
				}
			}
			
		}
		
		return true;
	}
	
	/*
	 * Creates a random number between 0 and max ([0, max>)
	 */
	private int getRandNum(int max)
	{
		return (int)(Math.random() * max);
	}
	
	// Selects a random partner, based on the available choices.
	private int selectPartner(boolean doneNorth, boolean doneEast, boolean doneSouth, boolean doneWest)
	{
		int numchoices = 0;
		
		// No partners left to do?
		if(doneNorth && doneEast && doneSouth && doneWest)
			return -1;
		
		// Add a choice for each still unchecked partner.
		if(!doneNorth)
			numchoices++;
		if(!doneEast)
			numchoices++;
		if(!doneSouth)
			numchoices++;
		if(!doneWest)
			numchoices++;
		
		// Choose
		int choice = (int) (Math.random() * numchoices);
		
		// If a partner is available, it was added to the choices
		if(!doneNorth)
		{
			// This is our choice
			if(choice == 0)
				return 0;
			// Prepare for the next
			else
				choice--;
		}
		if(!doneEast)
		{
			if(choice == 0)
				return 1;
			else
				choice--;
		}
		if(!doneSouth)
		{
			if(choice == 0)
				return 2;
			else
				choice--;
		}
		if(!doneWest)
		{
			if(choice == 0)
				return 3;
			else
				choice--;
		}
		/*!NOTREACHED*/
		return -1;
	}
	
	/**
	 * Find the first element with the solve state and returns it
	 * in xpos and ypos
	 */
	private boolean resetSolvestate(int solvestate)
	{
		int xSize = maze.getSizeX();
		int ySize = maze.getSizeY();
		
		for(int x = 0; x < xSize; x++)
		{
			for(int y = 0; y < ySize; y++)
			{
				if(maze.getSolveState(x, y) == solvestate)
				{
					maze.setSolveState(x, y, Maze.SOLVE_UNVISITED);
				}
			}
		}
		
		return true;
	}
}
